﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace LegtionMVVM 
{
    public class DelegatedReflection
    {
        private static Dictionary<string, INamedMemberAccessor> accessorCache = new Dictionary<string, INamedMemberAccessor>();

        public object GetValue(object instance, string memberName)
        {
            return FindAccessor(instance, memberName ).GetValue(instance);
        }

        public void SetValue(object instance, string memberName, object newValue)
        {

            INamedMemberAccessor accessor = FindAccessor(instance, memberName);
            accessor.SetValue(instance, newValue);
        }

        private INamedMemberAccessor FindAccessor(object instance, string memberName)
        {
            Type type = instance.GetType();
            string key = type.FullName + memberName;
            INamedMemberAccessor accessor;
            accessorCache.TryGetValue(key, out accessor);
            if (accessor == null)
            {
                PropertyInfo propertyInfo=Source.GetProperty(type, memberName);
 
                accessor = Activator.CreateInstance(
                    typeof(PropertyAccessor<,>)
                    .MakeGenericType(type, propertyInfo.PropertyType),
                    type, memberName) as INamedMemberAccessor;
                accessorCache.Add(key, accessor);
            }
 
            return accessor;
        }
    }

    internal interface INamedMemberAccessor
    {
        object GetValue(object instance);

        void SetValue(object instance, object newValue);
        
    }

    internal class PropertyAccessor<T, P> : INamedMemberAccessor
    {
        private Func<T, P> GetValueDelegate;
        private Action<T, P> SetValueDelegate;
 
        public PropertyAccessor(Type type, string propertyName)
        {
            PropertyInfo propertyInfo = Source.GetProperty(type, propertyName);
            if (propertyInfo != null)
            {
                GetValueDelegate = (Func<T, P>)Delegate.CreateDelegate(typeof(Func<T, P>), propertyInfo.GetGetMethod());
                SetValueDelegate = (Action<T, P>)Delegate.CreateDelegate(typeof(Action<T, P>), propertyInfo.GetSetMethod());
               
            }
        }

        public object GetValue(object instance)
        {
            return GetValueDelegate((T)instance);
        }

        public void SetValue(object instance, object newValue)
        {            
            SetValueDelegate((T)instance, (P)newValue);
        }

        #region INamedMemberAccessor 成员

 
        #endregion
    }
}
